#ifndef MATRIX2_H
#define MATRIX2_H

#include "Math/Math.h"

#include "Math/Vector2.h"
#include "Math/Vector3.h"
#include "Math/Vector4.h"


namespace tyro
{
	namespace Math
	{

		template < class T >
		class Matrix2
		{

		public:

			/* Constructors */
			Matrix2<T>(){}
			inline 				Matrix2<T>( const T* mat ); 
			inline				Matrix2<T>( T _00, T _01, 
											T _10, T _11);
			inline				Matrix2<T>( const Matrix2<T>& mat );	


			/* Operator Overloaders */

			/* Casting Operators*/
			inline 				operator T* ();
			inline 				operator const T* () const;

			/* Access Operators */
			inline T&			operator () ( unsigned int row, unsigned int column );
			inline T			operator () ( unsigned int row, unsigned int column ) const;

			/* Assignment Operators */

			/* Relational and Equality Operators */
			inline bool			operator == ( const Matrix2<T>& mat ) const;
			inline bool			operator != ( const Matrix2<T>& mat ) const;

			/* Binary Arithmetic Operators */
			inline const Matrix2<T> operator - () const;
			inline const Matrix2<T> operator + ( const Matrix2<T>& mat ) const;
			inline const Matrix2<T> operator - ( const Matrix2<T>& mat ) const;
			inline const Matrix2<T> operator * ( T value ) const;
			inline const Matrix2<T> operator * ( const Matrix2<T>& mat ) const;


			//-----------------------------------------------------------------------------
			// Friend multiply row Vector2 by matrix operator.
			//-----------------------------------------------------------------------------
			inline friend const Vector2<T> operator * ( const Vector2<T>& vec, const Matrix2<T>& mat )
			{

				Vector2<T> ans;
				ans.x = vec.x * mat.m00 + vec.y * mat.m10;
				ans.y = vec.x * mat.m01 + vec.y * mat.m11;
				return ans;
			}

			//-----------------------------------------------------------------------------
			// Friend multiply row Vector3 by matrix operator. W is assumed to be 1
			//-----------------------------------------------------------------------------
			inline friend const Vector3<T> operator * ( const Vector3<T>& vec, const Matrix2<T>& mat )
			{
				Vector3<T> ans;
				ans.x = vec.x * mat.m00 + vec.y * mat.m10;
				ans.y = vec.x * mat.m01 + vec.y * mat.m11;
				ans.z = 0.0f;
				return ans;
			}

			//-----------------------------------------------------------------------------
			// Friend multiply row Vector4 by matrix operator.
			//-----------------------------------------------------------------------------
			inline friend const Vector4<T> operator * ( const Vector4<T>& vec, const Matrix2<T>& mat )
			{
				Vector4<T> ans;
				ans.x = vec.x * mat.m00 + vec.y * mat.m10;
				ans.y = vec.x * mat.m01 + vec.y * mat.m11;
				ans.z = 0.0f;
				ans.w = 0.0f;
				return ans;
			}

			

			/* Compound Assignment Operators */
			inline Matrix2<T>&		operator += ( const Matrix2<T>& mat );
			inline Matrix2<T>&		operator -= ( const Matrix2<T>& mat );
			inline Matrix2<T>&		operator *= ( T value );
			inline Matrix2<T>&		operator *= ( const Matrix2<T>& mat );


			inline void Add( const Matrix2<T>& matA, const Matrix2<T>& matB );
			inline void Subtract( const Matrix2<T>& matA, const Matrix2<T>& matB );
			inline void	Multiply( const Matrix2<T>& matA, const Matrix2<T>& matB );

			/* Matrix 2 data members */
			union
			{
				T			m[ 2 ][ 2 ];
				T			mat[ 4 ];
				struct
				{
					T		m00, m01;
					T		m10, m11;
				};
			};

			/* Constants */
			static const Matrix2<T> ZERO;
			static const Matrix2<T> IDENTITY;

		};

		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline tyro::Math::Matrix2<T>::Matrix2( const T* mat ) 
		{
			memcpy( this->mat, mat, sizeof( this->mat ) );
		}

		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline	tyro::Math::Matrix2<T>::Matrix2( T _00, T _01,
												T _10, T _11 )
		{
			( *this ).mat[ 0 ] = _00; ( *this ).mat[ 1 ] = _01;  
			( *this ).mat[ 2 ] = _10; ( *this ).mat[ 3 ] = _11;  
		}
		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline tyro::Math::Matrix2<T>::Matrix2( const Matrix2<T>& mat )
		{
			memcpy( this, &mat, sizeof( Matrix2<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Casting operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>::operator T*()
		{
			return reinterpret_cast<T*>(this);
		}

		//-----------------------------------------------------------------------------
		// Const casting operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>::operator const T*() const
		{
			return reinterpret_cast<const T*>(this);
		}

		//-----------------------------------------------------------------------------
		// Access operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline T& Matrix2<T>::operator()( unsigned int row, unsigned int column )
		{
			assert( row < 2  &&  column < 2 );
			return m[ row ][ column ];
		}

		//-----------------------------------------------------------------------------
		// Const access operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline T Matrix2<T>::operator()( unsigned int row, unsigned int column ) const
		{
			assert( row < 2  &&  column < 2 );
			return m[ row ][ column ];
		}

		//-----------------------------------------------------------------------------
		// Equal to
		//-----------------------------------------------------------------------------
		template < class T >
		inline bool Matrix2<T>::operator == ( const Matrix2<T>& mat ) const
		{
			return 0 == memcmp( this, &mat, sizeof( Matrix2<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Not Equal to
		//-----------------------------------------------------------------------------
		template < class T >
		inline bool Matrix2<T>::operator != ( const Matrix2<T>& mat ) const
		{
			return 0 != memcmp( this, &mat, sizeof( Matrix2<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Unary minus
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix2<T> Matrix2<T>::operator - () const 
		{
			return Matrix2<T>(	-m00, -m01,
								-m10, -m11);
		}


		//-----------------------------------------------------------------------------
		// Addition operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix2<T> Matrix2<T>::operator + ( const Matrix2<T>& mat ) const
		{
			Matrix2<T> ans;
			ans.Add( *this, mat );
			return ans;
		}

		//-----------------------------------------------------------------------------
		// Matrix addition
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix2<T>::Add( const Matrix2<T>& matA, const Matrix2<T>& matB )
		{
			m00 = matA.m00 + matB.m00; m01 = matA.m01 + matB.m01; 
			m10 = matA.m10 + matB.m10; m11 = matA.m11 + matB.m11; 
		}

		//-----------------------------------------------------------------------------
		// Addition Assignment
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>& Matrix2<T>::operator += ( const Matrix2<T>& mat )
		{
			m00 += mat.m00 ; m01 += mat.m01 ; 
			m10 += mat.m10 ; m11 += mat.m11 ; 
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Subtraction 
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix2<T> Matrix2<T>::operator - ( const Matrix2<T>& mat ) const
		{
			Matrix2<T> ans;
			ans.Subtract( *this, mat );
			return ans;
		}

		//-----------------------------------------------------------------------------
		// Matrix subtraction
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix2<T>::Subtract( const Matrix2<T>& matA, const Matrix2<T>& matB )
		{
			m00 = matA.m00 - matB.m00; m01 = matA.m01 - matB.m01;  
			m10 = matA.m10 - matB.m10; m11 = matA.m11 - matB.m11;  
		}

		//-----------------------------------------------------------------------------
		// Subtraction assignment operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>& Matrix2<T>::operator -= ( const Matrix2<T>& mat )
		{
			m00 -= mat.m00 ; m01 -= mat.m01 ; 
			m10 -= mat.m10 ; m11 -= mat.m11 ; 
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Scalar multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix2<T> Matrix2<T>::operator * ( T value ) const
		{
			return Matrix2(	m00 * value , m01 * value ,
							m10 * value , m11 * value );
		}

		//-----------------------------------------------------------------------------
		// Scalar multiplication Assignment
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>& Matrix2<T>::operator *= ( T value )
		{
			m00 *= value; m01 *= value; 
			m10 *= value; m11 *= value;
			return *this;
		}
		
		//-----------------------------------------------------------------------------
		// Matrix multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix2<T>& Matrix2<T>::operator *= ( const Matrix2<T>& mat )
		{
			*this = (*this) * mat;
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Matrix multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix2<T> Matrix2<T>::operator * ( const Matrix2<T>& mat ) const
		{
			Matrix2<T> ans;
			ans.Multiply( *this, mat );
			return ans;
		}

		//-----------------------------------------------------------------------------
		// Matrix multiplication
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix2<T>::Multiply( const Matrix2<T>& matA, const Matrix2<T>& matB )
		{
			m00 = matA.m00 * matB.m00; m01 = matA.m01 * matB.m01; 
			m10 = matA.m10 * matB.m10; m11 = matA.m11 * matB.m11; 
		}

		

	}
}


#endif